//////////////////////////////////////////////////////////////

//SOURCE FILE: listas.c

//PROCESO PLANIFICADOR DE DISCO

//Autor: Pablo Bergna

//////////////////////////////////////////////////////////////
#include "../includes/todo_incluido.h"
#include "../includes/listas.h"



/***********************************************************************
FUNCION: CREAR_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE:

POST: lista creada y con memoria alocada

DEVUELVE:	0 si salio bien
			-1 si hubo error al alocar memoria

FUNCION PPAL: crea una lista dado un puntero a lista sin memoria y un tamanio de bloques de datos para sus nodos
***********************************************************************/
uint8_t crear_lista(td_lista* lista,uint16_t tamanio_bloque, void* func_comparacion){
	lista = (td_lista*)malloc(sizeof(td_lista));
		if (!(lista)) return -1;//ERROR AL ALOCAR MEMORIA
		lista->tam_bloque = tamanio_bloque;
		lista->actual = NULL;
		lista->primero = NULL;
		lista->ultimo = NULL;
		lista->func_comparacion = func_comparacion;
		return 0;
}

/***********************************************************************
FUNCION: LISTA_VACIA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y alocada en memoria

POST: 	LISTA_VACIA si no hay nodos en la lista
		LISTA_NO_VACIA si hay algun nodo en la lista

DEVUELVE:

FUNCION PPAL: se fija si hay nodos en la lista agregados
***********************************************************************/
uint8_t lista_vacia (td_lista* lista){
	if ((lista->actual == NULL) && (lista->primero == NULL) && (lista->ultimo)) return LISTA_VACIA; //ESTA VACIA!
	return LISTA_NO_VACIA;//NO ESTA VACIA!
}

/***********************************************************************
FUNCION: DESTRUIR_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y alocada en memoria

POST: Elimina toda la lista y libera memoria

DEVUELVE:

FUNCION PPAL: elimina la lista y todos sus nodos (libera toda su memoria)
***********************************************************************/
void destruir_lista (td_lista* lista){
	while (lista_vacia(lista) == LISTA_NO_VACIA){
		destruir_nodo_actual(lista);
	}
	free(lista);
}

/***********************************************************************
FUNCION: DESTRUIR_NODO

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y alocada en memoria (puede estar vacia)

POST: Elimina al nodo actual de la lista, si esta vacia no hace nada
	el nuevo actual sera (por descarte y en este orden):
						*El de delante del actual
						*El anterior al actual (si proximo de actual es null (actual era el primero)
						*NULL (si actual era el unico nodo en la lista)

DEVUELVE:

FUNCION PPAL: elimina al nodo actual de la lista (y libera su memoria)
***********************************************************************/
void destruir_nodo_actual(td_lista* lista){
	td_nodo_lista* aux;

	if (lista_vacia(lista) == LISTA_VACIA) return;
	aux = lista->actual;//GUARDO AL QUE VOY A BORRAR
	//CASO DE ACTUAL COMO UNICO EN LA LISTA
	if ((aux->proximo == NULL) && (aux->anterior == NULL)){
		lista->actual = NULL;
		lista->primero = NULL;
		lista->ultimo = NULL;
	}
	//CASO DE ACTUAL EN EL MEDIO DE LA LISTA
	if ((aux->proximo != NULL) && (aux->anterior != NULL)){
		lista->actual->anterior->proximo = lista->actual->proximo;//ENLAZO AL PROXIMO DEL ANTERIOR
		lista->actual->proximo->anterior = lista->actual->anterior;//ENLAZO EL ANTERIOR DEL PROXIMO
		lista->actual = lista->actual->proximo;//CAMBIO EL CORRIENTE AL PROXIMO
	}
	//CASO DE ACTUAL COMO EL PRIMERO DE LA LISTA
	if ((aux->proximo == NULL) && (aux->anterior != NULL)){
		lista->actual->anterior->proximo = NULL;//ENLAZO AL PROXIMO DEL ANTERIOR CON NULL
		lista->actual = lista->actual->anterior;//CAMBIO EL CORRIENTE AL ANTERIOR
		lista->primero = lista->actual;//ACTUALIZO EL PRIMERO COMO EL ACTUAL
	}
	//CASO DE ACTUAL COMO EL ULTIMO DE LA LISTA
	if ((aux->proximo != NULL) && (aux->anterior == NULL)){
		lista->actual->proximo->anterior = NULL;//ENLAZO AL ANTERIOR DEL PROXIMO CON NULL
		lista->actual = lista->actual->proximo;//CAMBIO EL CORRIENTE AL PROXIMO
		lista->ultimo = lista->actual;//ACTUALIZO EL ULTIMO COMO EL ACTUAL
	}

	free(aux->datos);//LIBERA LOS DATOS DEL NODO
	free(aux);//LIBERA LA ESTRUCTURA DEL NODO
}

/***********************************************************************
FUNCION: AGREGAR_NODO_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada, *datos conteniendo estructura del tamaño de bloque

POST: Agrega a la lista el nodo conteniendo *datos en la posicion marcada respecto al actual
		EL NODO INSERTADO ES EL NUEVO ACTUAL

DEVUELVE: 	0 si salio bien
			-1 si hubo error al alocar memoria
			-2 otro error no clasificado

FUNCION PPAL: agrega un nodo a la lista
***********************************************************************/
uint8_t agregar_nodo_lista(td_lista* lista, void* datos,uint8_t posicion){
	td_nodo_lista* aux;

	aux = (td_nodo_lista*)malloc(sizeof(td_nodo_lista));
	if (!(aux)) return -1;//SI FALLA AL ALOCAR MEMORIA
	aux->datos = (void*)malloc(lista->tam_bloque);
	if (!(aux->datos)){
		free(aux);
		return -1;//SI FALLA AL ALOCAR MEMORIA
	}
	//LLENADO DE DATOS
	memcpy(aux->datos,datos,lista->tam_bloque);

	//CASO DE LISTA VACIA
	if (lista_vacia(lista) == LISTA_VACIA){
		lista->actual = aux;
		lista->primero = aux;
		lista->ultimo = aux;
		aux->anterior = NULL;
		aux->proximo = NULL;

		return 0;//SALIO TODO BIEN
	}
	//CASOS DE INSERCION DE LISTA: (TENIENDO EN CUENTA LA POSICION A INSERTAR RESPECTO DEL ACTUAL
	//CASO DE INSERCION EN LA MITAD DE LA LISTA
	if ((lista->actual->proximo != NULL) && (lista->actual->anterior != NULL)){
		if (posicion == ADELANTE){
			aux->proximo = lista->actual->proximo;
			aux->anterior = lista->actual;
			lista->actual->proximo->anterior = aux;
			lista->actual->proximo = aux;
			lista->actual = aux;
			return 0;//SALIO TODO BIEN
		}
		if (posicion == ATRAS){
			aux->proximo = lista->actual;
			aux->anterior = lista->actual->anterior;
			lista->actual->anterior->proximo = aux;
			lista->actual->anterior = aux;
			lista->actual = aux;
			return 0;//SALIO TODO BIEN
		}
	}//FIN CASO DE INSERCION EN MEDIO DE LA LISTA

	//CASO DE INSERCION EN PRINCIPIO DE LA LISTA (CON ANTERIOR EXISTENTE)
	if ((lista->actual->proximo == NULL) && (lista->actual->anterior != NULL)){
		if (posicion == ADELANTE){
			aux->proximo = NULL;
			aux->anterior = lista->actual;
			lista->actual->proximo = aux;
			lista->actual = aux;
			lista->primero = aux;
			return 0;//SALIO TODO BIEN
		}
		if (posicion == ATRAS){
			aux->proximo = lista->actual;
			aux->anterior = lista->actual->anterior;
			lista->actual->anterior->proximo = aux;
			lista->actual->anterior = aux;
			lista->actual = aux;
			return 0;//SALIO TODO BIEN
		}
	}//FIN CASO DE INSERCION EN PRINCIPIO DE LA LISTA

	//CASO DE INSERCION AL FINAL DE LA LISTA (CON PROXIMO EXISTENTE)
	if ((lista->actual->proximo != NULL) && (lista->actual->anterior == NULL)){
		if (posicion == ADELANTE){
			aux->proximo = lista->actual->proximo;
			aux->anterior = lista->actual;
			lista->actual->proximo->anterior = aux;
			lista->actual->proximo = aux;
			lista->actual = aux;
			return 0;//SALIO TODO BIEN
		}
		if (posicion == ATRAS){
			aux->proximo = lista->actual;
			aux->anterior = NULL;
			lista->actual->anterior = aux;
			lista->actual = aux;
			lista->ultimo = aux;
			return 0;//SALIO TODO BIEN
		}
	}//FIN CASO DE INSERCION AL FINAL DE LA LISTA

	//CASO DE INSERCION CON UNICO NODO EN LA LISTA
	if ((lista->actual->proximo == NULL) && (lista->actual->anterior == NULL)){
		if (posicion == ADELANTE){
			aux->proximo = NULL;
			aux->anterior = lista->actual;
			lista->actual->proximo = aux;
			lista->actual = aux;
			lista->primero = aux;
			return 0;//SALIO TODO BIEN
		}
		if (posicion == ATRAS){
			aux->proximo = lista->actual;
			aux->anterior = NULL;
			lista->actual->anterior = aux;
			lista->actual = aux;
			lista->ultimo = aux;
			return 0;//SALIO TODO BIEN
		}
	}//FIN CASO DE INSERCION CON UNICO NODO EN LA LISTA

	return -2;//SI LLEGO HASTA ACA NO ENTRO A NINGUN IF BIEN

}

/***********************************************************************
FUNCION: SACAR_NODO_ACTUAL

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada

POST: devuelve en *datos lo que contenía el nodo actual de la lista, SACANDO AL NODO DE LA LISTA

DEVUELVE: 	0 si salio bien
			-1 si hubo error al alocar memoria
			-2 si la lista está vacía (no aloca memoria para *datos)

FUNCION PPAL: copia los datos del nodo actual en el puntero *datos (y le asigna memoria)
***********************************************************************/
uint8_t sacar_nodo_actual(td_lista* lista, void* datos){

	if (lista_vacia(lista) == LISTA_VACIA) return -2;
	datos = (void*)malloc(lista->tam_bloque);
	if (!(datos)) return -1;//SI FALLA AL ALOCAR MEMORIA

	//COPIA DE DATOS
	memcpy(datos,lista->actual->datos,lista->tam_bloque);

	destruir_nodo_actual(lista);//ELIMINA AL NODO ACTUAL

	return 0;//SALIO TODO BIEN
}

/***********************************************************************
FUNCION: OBTENER_NODO_ACTUAL

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada

POST: devuelve en *datos lo que contenía el nodo actual de la lista, SIN SACARLO DE LA LISTA

DEVUELVE: 	0 si salio bien
			-1 si hubo error al alocar memoria
			-2 si la lista está vacía (no aloca memoria para *datos)

FUNCION PPAL: copia los datos del nodo actual en el puntero *datos (y le asigna memoria)
***********************************************************************/
uint8_t obtener_nodo_actual(td_lista* lista, void* datos){

	if (lista_vacia(lista) == LISTA_VACIA) return -2;
	datos = (void*)malloc(lista->tam_bloque);
	if (!(datos)) return -1;//SI FALLA AL ALOCAR MEMORIA

	//COPIA DE DATOS
	memcpy(datos,lista->actual->datos,lista->tam_bloque);

	return EXIT_SUCCESS;//SALIO TODO BIEN
}

/***********************************************************************
FUNCION: MOVER_ACTUAL_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada

POST: cambia el nodo actual de la lista al indicado en posicion (solo mueve de a 1 nodo)

DEVUELVE: 	0 si salio bien
			-1 si la lista está vacía (no mueve nada)
			-2 movimiento no valido (no hace nada)

FUNCION PPAL: mueve el nodo actual de la lista en 1 nodo en la posicion indicada
***********************************************************************/
uint8_t mover_actual_lista(td_lista* lista, uint8_t posicion){

	if (lista_vacia(lista) == LISTA_VACIA) return -1;

	if (posicion == ADELANTE){
		if (lista->actual->proximo != NULL){
			lista->actual = lista->actual->proximo;
		}else return -2;
	}
	if (posicion == ATRAS){
		if (lista->actual->anterior != NULL){
			lista->actual = lista->actual->anterior;
		}else return -2;
	}

	return EXIT_SUCCESS;//SALIO TODO BIEN
}

/***********************************************************************
FUNCION: COMPARAR_NODOS_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada (y funcion de comparacion seteada)

POST: compara dos valores utilizando la funcion de comparacion seteada en la lista

DEVUELVE: 	TRUE si son iguales
			FALSE si no son iguales

FUNCION PPAL: compara dos valores usando la funcion de comparacion de la lista
***********************************************************************/
uint8_t comparar_nodos_lista(void* datoA, void* datoB,td_lista* lista){
	if (lista->func_comparacion(datoA,datoB) == TRUE) return TRUE;
	return FALSE;
}



/***********************************************************************
FUNCION: BUSCAR_NODO_LISTA

PROCESO: PPD

Autor: Pablo Bergna

PRE: *lista creada y con memoria alocada

POST: si encuentra el nodo buscado dentro de la lista queda como actual dicho nodo
	para buscar el nodo utiliza la funcion de comparacion de la lista con su puntero a funcion

DEVUELVE: 	EXIT_SUCCESS si salio bien y encontro el nodo
			EXIT_FAILURE si hubo algun error o no encontro el nodo

FUNCION PPAL: busca un nodo en la lista y si lo encuentra queda como actual
***********************************************************************/
uint8_t buscar_nodo_lista(void* buscado, td_lista* lista){

	uint8_t err = 0;
	void* aux = NULL;
	uint8_t flag = 0;
	td_nodo_lista* nodo_inicial;

	if (lista_vacia(lista) == LISTA_VACIA) return EXIT_FAILURE;

	err = obtener_nodo_actual(lista,aux);
	if (err != EXIT_SUCCESS) return EXIT_FAILURE;

	//CASO DE QUE HAYA UN UNICO NODO EN LA LISTA
	if (comparar_nodos_lista(aux,buscado,lista) == TRUE) return EXIT_SUCCESS;

	//Guardo la posicion antes de empezar la busqueda por si no encuentra
	nodo_inicial = lista->actual;

	//mover al principio de la lista
	lista->actual = lista->primero;

	while (flag == 0){

		err = obtener_nodo_actual(lista,aux);
		if (err != EXIT_SUCCESS) return EXIT_FAILURE;

		if (comparar_nodos_lista(aux,buscado,lista) == TRUE) flag = 1;
		else{
			 if (mover_actual_lista(lista, ADELANTE) != EXIT_SUCCESS) flag = 2;
		}
	}

	if (flag == 2) {
		//restauramos el actual de la lista a como estaba antes de empezar
		lista->actual = nodo_inicial;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

